import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:crypto/crypto.dart';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter_miaoyin_sound/utils/appinfo_manage_utils.dart';
import 'package:flutter_miaoyin_sound/utils/dialog_util.dart';
import 'package:flutter_miaoyin_sound/utils/encrypt_decrypt_utils.dart';
import 'package:flutter_miaoyin_sound/utils/log_utils.dart';
import 'package:flutter_miaoyin_sound/utils/publish_utils.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart' as getx;
import '../../config/dialog_config.dart';
import '../../config/http_error_code.dart';
import '../../page/setting/about/contact_service_page.dart';
import '../../page/webview/webview_page.dart';
import '../../utils/sp_util.dart';
import '../../utils/string_util.dart';
import '../../utils/theme_text_style_util.dart';
import '../netconnect/network_connection.dart';
import '../../utils/domain_manager_utils.dart';
import '../../utils/login_manage_utils.dart';
import '../notification/event_bus.dart';
import 'base_response.dart';
import 'dio_intercept.dart';
import 'dio_wrapper.dart';
import 'http_url_config.dart';


class HttpUtils {
  static final HttpUtils _instance = HttpUtils._();

  static HttpUtils get instance => HttpUtils();

  factory HttpUtils() => _instance;

  static late Dio _dio;

  Dio get dio => _dio;

  // 设置请求重试次数（请求超时15秒）
  static int retries = 3;

  // late String _randomSignKey;

  HttpUtils._() {

    final BaseOptions options = BaseOptions(
        connectTimeout: const Duration(milliseconds: 15000),
        receiveTimeout: const Duration(milliseconds: 15000),
        sendTimeout: const Duration(milliseconds: 15000),
        responseType: ResponseType.json,
        baseUrl: DomainManagerUtils().mainBaseUrl,
        // headers: createCommonHeaders(),
        validateStatus: (_) => true
    );

    //实例化 Dio
    _dio = Dio(options);

    // // 抓包代理
    // String proxy = '';
    // var isProxy = SpUtils.getBool("isProxyConfigure");
    // if(isProxy) {
    //   var server = SpUtils.getString("ipConfigure");
    //   var port = SpUtils.getString("portConfigure");
    //   proxy = "PROXY $server:$port";
    // }
    //
    // (_dio.httpClientAdapter as IOHttpClientAdapter).onHttpClientCreate =
    //     (HttpClient client) {
    //       if (proxy.length > 7) {
    //         client.findProxy = (url) {
    //           return proxy;
    //         };
    //       }
    //       client.badCertificateCallback =
    //       (X509Certificate cert, String host, int port) => true;
    //   return client;
    // };

    //添加迭代器
    _dio.interceptors.add(AuthInterceptor());
    if (kDebugMode) {
      _dio.interceptors.add(LoggingInterceptor());
    }
    _dio.interceptors.add(ErrorInterceptor());
  }

  Future<Map<String, dynamic>> createCommonHeaders(Map<String, dynamic> headerSaltParams,
      { required String sign,
        required bool isNeedToken,
        required String nonce }) async {
    headerSaltParams['Content-Type'] = 'application/json';
    if(Platform.isAndroid) {
      headerSaltParams['User-Agent'] = 'Shiyin-Android';
    } else {
      headerSaltParams['User-Agent'] = 'Shiyin-iOS';
    }
    headerSaltParams['model'] = AppInfoManageUtils.machine;
    headerSaltParams['channel'] = AppInfoManageUtils.channelID;
    if(isNeedToken) { //接口需要传token
      if (LoginManageUtils().isLogin) {
        headerSaltParams['authorization'] =
            LoginManageUtils().user!.authorization;
      }
      else if (SpUtils.containsKey('api_authorization')) {
        headerSaltParams['authorization'] =
            SpUtils.getString('api_authorization');
      }
    }
    headerSaltParams['sign'] = sign;
    headerSaltParams['timestamp'] = DateTime.now().millisecondsSinceEpoch;
    headerSaltParams['nonce'] = nonce;
    return headerSaltParams;
  }

  //创建需要加盐的headers参数字典
  Map<String, dynamic> createSaltHeaders() {
    Map<String, dynamic> saltHeadersMap = {};
    saltHeadersMap['deviceid'] = AppInfoManageUtils.deviceId;
    saltHeadersMap['platform'] = AppInfoManageUtils.platformId;
    saltHeadersMap['appid'] = AppInfoManageUtils.appID;
    saltHeadersMap['version'] = AppInfoManageUtils.appVersion;
    //对randomSignKey进行rsa加密处理
    // saltHeadersMap['nonce'] = EnCryptDecryptUtils.encryptRSA(randomSignKey);
    return saltHeadersMap;
  }

  //产生length位随机数方法
  String generateRandomString(int length) {
    final random = Random();
    const chars = 'aceBDG1352469780';
    String result = '';

    for (int i = 0; i < length; i++) {
      final randomIndex = random.nextInt(chars.length);
      result += chars[randomIndex];
    }
    return result;
  }

  Future<BaseResponse> _request(String url, {
    String method = 'POST',
    dynamic params,
    dynamic body,
    bool isEncrypted = false,
    bool isNeedToken = true,
    void Function(int, int)? onSendProgress,
    void Function(int, int)? onReceiveProgress }) async {
    return _requestWithRetry(url, retries ,method:method,
        params:params,
        body:body,
        isEncrypted:isEncrypted,
        isNeedToken:isNeedToken,
        onSendProgress:onSendProgress,
        onReceiveProgress:onReceiveProgress);
  }

  Future<BaseResponse> _requestWithRetry(String url, int retries,{
    String method = 'POST',
    dynamic params,
    dynamic body,
    bool isEncrypted = false,
    bool isNeedToken = true,
    void Function(int, int)? onSendProgress,
    void Function(int, int)? onReceiveProgress }) async{
    try {
      // 发起请求
      Future<BaseResponse> response = _request_core(url,method:method,params:params,
          body:body,
          isEncrypted:isEncrypted,
          isNeedToken:isNeedToken,
          onSendProgress:onSendProgress, onReceiveProgress:onReceiveProgress);
      return response; // 请求成功，返回响应
    } catch (e) {
      // 捕获异常
      if (e is DioError && e.type == DioErrorType.receiveTimeout && retries > 0) {
        // 如果是超时异常且还有重试次数 ，则递归调用自己进行重试
        print('Request timed out, retrying... (retries left: $retries)');
        return await _requestWithRetry(url, retries - 1,method:method,params:params,
            body:body,
            isEncrypted:isEncrypted,
            isNeedToken:isNeedToken,
            onSendProgress:onSendProgress, onReceiveProgress:onReceiveProgress);
      } else {
        // 其他异常或没有重试次数了，重新抛出异常
        rethrow;
      }
    }
  }
  
  Future<BaseResponse> _request_core(String url, {
    String method = 'POST',
    dynamic params,
    dynamic body,
    bool isEncrypted = false,
    bool isNeedToken = true,
    void Function(int, int)? onSendProgress,
    void Function(int, int)? onReceiveProgress }) async {

    //检测账号是否已注销
    var logoffDetail = LoginManageUtils().user?.logOffApplyDetailVo;
    if(logoffDetail != null&&logoffDetail.estimatedTime != null){
      DateTime endTime = DateTime.parse(logoffDetail.estimatedTime!);
      if(endTime.compareTo(DateTime.now()) < 0){
        //如果结束日期已到期
        if(SpUtils.containsKey('api_authorization') ||
            LoginManageUtils().user != null) { //token过期后防止一次请求多个接口弹多次问题
          SmartDialog.dismiss();
          LoginManageUtils().clear();
          EventBus().emit('logout', {'status': 0}); //0表示登出成功，1表示登出失败
          DialogUtils.showCommonDialog(
            '下线提醒',
            content: '您的账号已注销，无法继续登录',
            isSingleBtn: true,
            showCloseBtn: false,
            okText: '已知晓',
          );
        }
        return BaseResponse(status: 500, msg: '账号已注销', data: '', success: false, resData: null);
      }
    }

    var connectResultFuture = NetworkConnection.instance.checkConnectivity();
    ConnectivityResult result = await connectResultFuture;
    if(result == ConnectivityResult.none) {
      return BaseResponse(status: 100, msg: '网络异常，请检查网络', data: '', success: false, resData: null);
    }
    var saltParams = {};
    if(params is Map && params.isNotEmpty) {
      saltParams = Map.from(params);
    }
    if(body is Map && body.isNotEmpty) {
      saltParams.addAll(Map.from(body));
    }
    //产生16位随机数
    final randomSignKey = generateRandomString(16);
    var nonce = EnCryptDecryptUtils.encryptRSA(randomSignKey); //对randomSignKey进行rsa加密处理
    var headerSaltParams = createSaltHeaders();//构造请求头需要加盐参数
    var sign = getSign(randomSignKey, headerSaltParams, saltParams, method);//加盐

    LogUtils.log('md5sign = $sign');
    _dio.options.headers = await createCommonHeaders(headerSaltParams,
        sign: sign,
        isNeedToken: isNeedToken,
        nonce: nonce);

    late BaseResponse response;
    try {
      late Response<dynamic> res;
      if(method == 'GET') {
        res = await _dio.get(url, queryParameters: params);
      } else if (method == 'DELETE') {
        res = await _dio.delete(url,queryParameters: params);
      } else if(method == 'UPLOAD') {
        FormData formData = FormData.fromMap(params);
        res = await _dio.post(url, data: formData,
            onSendProgress: onReceiveProgress,
            onReceiveProgress: onReceiveProgress);
      } else if (method == 'PUT') {//PUT请求
        res = await _dio.put(url,data: body, queryParameters: params);
      } else { //POST请求
        // if(DomainManagerUtils().curConfigEnv == EnvironmentType.release && isEncrypted) { //如果isEncrypted=true，则对body进行AES加密
        if(isEncrypted) { //如果isEncrypted=true，则对body进行AES加密
          var aesEncrypt = EnCryptDecryptUtils.encryptAES(json.encode(params), randomSignKey);
          var dataDic = {"data" : aesEncrypt};
          res = await _dio.post(url, data: dataDic);
        } else {
          res = await _dio.post(url, data: params);
        }
      }
      if (isEncrypted) {
      // if (DomainManagerUtils().curConfigEnv == EnvironmentType.release && isEncrypted) {
        response = DioWrapper.responseWrapper(res, secretKey: randomSignKey);
      } else {
        response = DioWrapper.responseWrapper(res);
      }
    } catch (e) {
      LogUtils.log('exception: $e');
      response = DioWrapper.errorWrapper(e);
    } finally {
      //hide 13580513048
    }

    LogUtils.log('response: $response');
    if(url != HttpUrlConfig.logoutUrl && isNeedToken) { ///手动退出登录和不需要token接口不处理
      _dealHttpResponseStatus(url,response);
    }
    return response;
  }

  void _dealHttpResponseStatus(String url,BaseResponse res) {
    String msg = res.msg.isNotEmpty ? res.msg : '登录已失效,请重新登录';
    switch (res.status) {
      case 10099:   ///未提供token
      case 10100:   ///invalid_token
      case 10101:   ///未登录/登录已过期
      case 10102:   ///已在其他地方登录
      case 10103:   ///当前登录被强制登出
        if(SpUtils.containsKey('api_authorization') ||
            LoginManageUtils().user != null) { //token过期后防止一次请求多个接口弹多次问题
          SmartDialog.dismiss();
          LoginManageUtils().clear();
          EventBus().emit('logout', {'status': 0}); //0表示登出成功，1表示登出失败
          DialogUtils.showCommonDialog(
            '登录失效',
            content: msg,
            okText: '重新登录',
            onOkTap: () => LoginManageUtils().loginJumpToPage(),
          );
        }
        break;
      case HttpErrorCode.featForbidden:   ///功能被封禁
        if(url == HttpUrlConfig.publishCommentUrl||url == HttpUrlConfig.replyCommentUrl){
          //发布评论
          PublishUtils.showBannedTips(tailTips: '评论');
        }else {
          PublishUtils.showBannedTips();
        }
        break;
      case HttpErrorCode.accountForbidden:  ///账号被封禁
        {
          String? expiredAt;
          if(res.data is Map) {
            expiredAt = res.data['expiredAt'];
          }
          showBannedTips(banExpiredTime: expiredAt);
        }
        break;
    }
  }

  ///显示账号封禁提示框
  void showBannedTips({String? banExpiredTime}) {
    String banMsg = '已被系统封禁';
    if(banExpiredTime != null) {
      if(banExpiredTime! == '永久') {
        banMsg = '已被系统永久封禁';
      } else if(banExpiredTime!.isNotEmpty) {
        banMsg = '已被系统封禁至${banExpiredTime!}';
      }
    }
    TextStyle textStyle = ThemeTextStyleUtil.pingFangMediumFontTextStyle(fontSize: 14);
    TextStyle textHighLightStyle = ThemeTextStyleUtil.pingFangMediumFontTextStyle(
        textColor: const Color(0xFFBF3F3F), fontSize: 14);

    var contentWidget = StringUtil.getRichText([
      TextSpan(text: "该账号涉嫌违反", style: textStyle),
      TextSpan(text: " 《是音社区自律公约》 ", style: textHighLightStyle,  recognizer: TapGestureRecognizer()
        ..onTap = () {
          getx.Get.to(() => WebViewPage.fromUrlString(
            DomainManagerUtils().selfRegulationPactUrl,
            pageTitle: '是音社区自律公约',
            fixedPageTitle: true,
            isAllowBack: true,
          ));
          SmartDialog.dismiss(
              status: SmartStatus.dialog);
        }),
      TextSpan(text: "，$banMsg。如有疑问,请联系官方客服申诉，解除后，系统会恢复账号。", style: textStyle),
    ], textAlign: TextAlign.center);

    DialogUtils.showNoSubTitleDialog("账号封禁通知",
        contentWidget: contentWidget,
        titleTextAlign: TextAlign.center,
        tag: DialogConfig.switchEnvironmentTag,
        okText: "联系客服", () {
          getx.Get.to(() => ContactServicePage(fromPath: '账号封禁_联系客服',));
        });
  }

  Map<dynamic, dynamic> removeNullForMap(Map<dynamic, dynamic> saltParams) {
    var sortedKeys = saltParams.keys.toList()..sort();
    Map<dynamic, dynamic> result = {};
    for(var i = 0; i < sortedKeys.length; i++) {
      var key = sortedKeys[i];
      var value = saltParams[key];
      if(value != null) {
        result[key] = value;
      }
    }
    return result;
  }

  ///使用随机产生的randomSignKey，对头参数和查询参数进行加盐
  String getSign(String randomSignKey,
      Map<dynamic, dynamic> headerParams,
      Map<dynamic, dynamic> queryParams,
      String method) {

    //对请求头部分参数进行加盐
    var headerSortedKeys = headerParams.keys.toList()..sort();
    String signStr = randomSignKey;
    for(var i = 0; i < headerSortedKeys.length; i++) {
      var key = headerSortedKeys[i];
      var value = headerParams[key];
      signStr += '$key$value';
    }

    //对查询参数进行加盐
    var querySortedKeys = queryParams.keys.toList()..sort();
    for(var i = 0; i < querySortedKeys.length; i++) {
      var key = querySortedKeys[i];
      var value = queryParams[key];
      if(value is List || value is Map) { //如果参数值是列表不参与加盐
        continue;
      }
      // else if(value is Map) { //如果参数值是map，对应的key不参与加盐，对value排序
      //   var keys = value.keys.toList()..sort();
      //   for(var j = 0; j < keys.length; j++) {
      //     var k = keys[j];
      //     var v = value[k];
      //     signStr += '$k$v';
      //   }
      // }
      else {
        signStr += '$key$value';
      }
    }
    signStr += randomSignKey;
    // if(method.toUpperCase() == 'GET') {
    //   signStr = Uri.encodeComponent(signStr);
    // }
    LogUtils.log('sign = $signStr');
    var sign = md5.convert(utf8.encode(signStr)).toString();
    return sign;
  }

  //DELETE
  Future<BaseResponse> delete(String url, {dynamic params, bool isEncrypted=false,
    bool isNeedToken = true}) async {
    return _request(url, method: 'DELETE', params: params,
        isEncrypted: isEncrypted, isNeedToken: isNeedToken);
  }

  //GET
  Future<BaseResponse> get(String url, {dynamic params, bool isEncrypted=false,
    bool isNeedToken = true}) async {
    return _request(url, method: 'GET', params: params,
        isEncrypted: isEncrypted, isNeedToken: isNeedToken);
  }

  //PUT
  Future<BaseResponse> put(String url, {dynamic params,dynamic body, bool isNeedToken=true}) async {
    return _request(url, method: 'PUT', params: params,body: body, isNeedToken: isNeedToken);
  }

  //POST
  ///isEncrypted 是否对body加密
  Future<BaseResponse> post(String url, {dynamic params, bool isEncrypted=false,
    bool isNeedToken = true}) async {
    return _request(url, method: 'POST', params: params,
        isEncrypted: isEncrypted, isNeedToken: isNeedToken);
  }

  //UPLOAD
  Future<BaseResponse> upload(String url, {dynamic params, bool isEncrypted=false,
  bool isNeedToken=true,
  void Function(int, int)? onSendProgress,
  void Function(int, int)? onReceiveProgress}) async {
    return _request(url, method: 'UPLOAD', params: params,
        isEncrypted: isEncrypted,
        isNeedToken: isNeedToken,
        onSendProgress: onSendProgress,
        onReceiveProgress: onReceiveProgress);
  }
}